package app.server.project.main.service.service;

import app.server.project.core.constants.TcpCmd;
import app.server.project.core.websocket.WSMgr;
import app.server.project.main.service.bo.event.DayRankEvent;
import app.server.project.main.service.bo.event.RaceValEvent;
import app.server.project.main.service.bo.event.WeekRankEvent;
import app.server.project.main.service.db.dao.cfg.RaceCfgDao;
import app.server.project.main.service.db.dao.user.UserInfoDao;
import app.server.project.main.service.db.dao.user.UserRaceDao;
import app.server.project.main.service.db.entity.cfg.RaceCfgEntity;
import app.server.project.main.service.db.entity.user.UserInfoEntity;
import app.server.project.main.service.db.entity.user.UserRaceEntity;
import app.server.project.main.service.dto.race.AiCoinDto;
import app.server.project.main.service.dto.race.RaceValDto;
import app.server.project.main.service.service.race.CoinType;
import lombok.AllArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class ResService {


    private UserRaceDao userRaceDao;
    private UserInfoDao userInfoDao;
    private RaceCfgDao raceCfgDao;
    private ApplicationEventPublisher publisher;

    public AiCoinDto getAiCoinDto(long userId) {
        UserInfoEntity userInfoEntity = this.userInfoDao.getUserInfoById(userId);
        RaceCfgEntity raceCfgEntity = this.raceCfgDao.getAiCoin();
        return AiCoinDto.of(userInfoEntity, raceCfgEntity);
    }

    public RaceValDto getRaceValDto(UserRaceEntity userRaceEntity) {
        RaceCfgEntity raceCfgEntity = this.raceCfgDao.getOne(userRaceEntity.getRaceId());
        return RaceValDto.of(userRaceEntity, raceCfgEntity);
    }

    public void pushAiCoin(long userId) {
        var dto = this.getAiCoinDto(userId);
        WSMgr.push(userId, TcpCmd.AiCoin, dto);
    }

    public void pushRaceValDto(long userId, UserRaceEntity userRaceEntity) {
        var dto = this.getRaceValDto(userRaceEntity);
        WSMgr.push(userId, TcpCmd.RaceVal, dto);
    }

    /**
     * 增加coin
     *
     * @param userId
     * @param num
     */
    public void addAICoin(long userId, long num, boolean push) {
        UserInfoEntity userInfoEntity = this.userInfoDao.getUserInfoById(userId);
        userInfoEntity.addAiCoin(num);
        var cfg = this.raceCfgDao.getAiCoin();
        this.publisher.publishEvent(DayRankEvent.of(cfg.getId(), userId, num));
        this.publisher.publishEvent(WeekRankEvent.of(cfg.getId(), userId, num));
        if (!push) return;
        this.pushAiCoin(userId);
    }

    public boolean consumeAiCoin(long userId, long num) {
        UserInfoEntity userInfoEntity = this.userInfoDao.getUserInfoById(userId);
        if (num > userInfoEntity.getAiCoin()) return false;
        userInfoEntity.consumeAiCoin(num);
        this.pushAiCoin(userId);
        return true;
        //var cfg = this.raceCfgDao.getAiCoin();
        //this.publisher.publishEvent(DayRankEvent.of(cfg.getId(), userId, -num));
        //this.publisher.publishEvent(WeekRankEvent.of(cfg.getId(), userId, -num));
    }

    public void addResNum(long userId, int raceId, long num, boolean push) {
        var aiCoinCfg = this.raceCfgDao.getAiCoin();
        if (aiCoinCfg.getId() == raceId) {
            this.addAICoin(userId, num, push);
        } else {
            this.addRaceNum(userId, raceId, num, push);
        }
    }


    public void addRaceNum(long userId, int raceId, long num, boolean push) {
        var list = this.userRaceDao.getRaceInfo(userId);
        var one = this.userRaceDao.getOne(list, raceId, userId);

        one.addVal(num);
        pubEvent(userId, raceId, num);
        if (!push) return;
        this.pushRaceValDto(userId, one);
    }


    private void pubEvent(long userId, int raceId, long num) {
        var cfg = this.raceCfgDao.getOne(raceId);
        this.publisher.publishEvent(DayRankEvent.of(cfg.getId(), userId, num));
        this.publisher.publishEvent(WeekRankEvent.of(cfg.getId(), userId, num));
        this.publisher.publishEvent(RaceValEvent.of(userId, raceId));
    }

    public boolean hasNum(long userId, long num, int raceId) {
        if (CoinType.AI_COIN == raceId) {
            UserInfoEntity userInfoEntity = this.userInfoDao.getUserInfoById(userId);
            return userInfoEntity.getAiCoin() >= num;
        } else {
            var list = this.userRaceDao.getRaceInfo(userId);
            var one = this.userRaceDao.getOne(list, raceId, userId);
            return one.getVal() >= num;
        }
    }

    public boolean consumeRaceNum(long userId, long num, int raceId) {
        if (CoinType.AI_COIN == raceId) {
            return this.consumeAiCoin(userId, num);
        } else {
            var list = this.userRaceDao.getRaceInfo(userId);
            var one = this.userRaceDao.getOne(list, raceId, userId);
            if (num > one.getVal()) return false;
            one.consumeVal(num);
            this.pushRaceValDto(userId, one);
            return true;
        }
    }
}
